﻿using System;

namespace StockLib.Indicator
{
    /***
	AX = 今天的收盘价 - N天中的最低价
	BX = N天中的最高价 - N天中的最低价
	RSV = （AX ÷ BX）× 100%
	NK = K计算天数，一般取3
	ND = D计算天数，一般取3
	K = 前一日K×(NK-1)/NK + RSV×1/NK 
	D = 前一日D×(ND-1)/ND + K×1/3
	J = 3D － 2K （或 J = 3K - 2D）
	第一次计算时，前一日的K、D值皆以50代替。
    */
    public partial class StockKdjIndicator : StockIndicator
    {
        private int rsvDays;
        private int kDays;
        private int dDays;

        public StockKdjIndicator()
            : base()
        {
            cacheCount = 3;
        }

        public override void SetDefaultParameters()
        {
            rsvDays = 9;
            kDays = 3;
            dDays = 3;
        }

        protected override float[] CalcOutput(int index)
        {
            //EMA(12)=前一日EMA(12)×11/13＋今日收盘价×2/13
            //EMA(26)=前一日EMA(26)×25/27＋今日收盘价×2/27 

            float[] outputValues = new float[3];
            outputValues[0] = CalcCloseEmaCache(index, rsvDays, 0) - CalcCloseEmaCache(index, kDays, 1);
            outputValues[1] = CalcDeaEmaCache(index, dDays);
            outputValues[2] = (outputValues[0] - outputValues[1]) * 2;
            return outputValues;
        }

        private float CalcDeaEmaCache(int index, int days)
        {
            if (!cacheValues[2].ContainsKey(index))
            {
                float val = CalcCloseEmaCache(index, rsvDays, 0) - CalcCloseEmaCache(index, kDays, 1);
                float ema;
                if (index <= 0)
                {
                    ema = val;
                }
                else
                {
                    float emaLast = CalcDeaEmaCache(index - 1, days);
                    ema = (2.0f / (days + 1)) * (val - emaLast) + emaLast;
                }
                cacheValues[2].Add(index, ema);
            }

            return cacheValues[2][index];
        }

        private float CalcCloseEmaCache(int index, int days, int cacheIndex)
        {
            if (cacheIndex < 0 || cacheIndex >= cacheCount)
                throw new IndexOutOfRangeException("缓冲索引超出范围");

            // 指数平均值 EMA(n)= n 日平滑系数 × （今日收盘价－昨日 EMA ） + 昨日 EMA 
            // n 日平滑系数 =2÷ （ n+1 ） 
            if (!cacheValues[cacheIndex].ContainsKey(index))
            {
                float val = kData[index].Close;
                float ema;
                if (index <= 0)
                {
                    ema = val;
                }
                else
                {
                    float emaLast = CalcCloseEmaCache(index - 1, days, cacheIndex);
                    ema = (2.0f / (days + 1)) * (val - emaLast) + emaLast;
                }

                cacheValues[cacheIndex].Add(index, ema);
            }

            return cacheValues[cacheIndex][index];
        }
    }
}
